/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "securec.h"
#include "sle_discover_seek_parcel.h"
#include "sle_hilog.h"

namespace OHOS {
namespace NearLink {
namespace SleStandard {
SeekParam::SeekParam(const SleSeekParam &param)
{
    addressType_ = param.addrType;
    repateFilter_ = param.repateFilter;
    filterPolicy_ = param.filterPolicy;
    phys_ = param.phys;
    for(size_t i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        type_[i] = param.type[i];
        interval_[i] = param.interval[i];
        window_[i] = param.window[i];
    }
}

bool SeekParam::Marshalling(Parcel &parcel) const
{
    if (!parcel.WriteUint8(addressType_)) {
        return false;
    }
    if (!parcel.WriteUint8(repateFilter_)) {
        return false;
    }
    if (!parcel.WriteUint8(filterPolicy_)) {
        return false;
    }
    if (!parcel.WriteUint8(phys_)) {
        return false;
    }
    for (int i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        if (!parcel.WriteUint8(type_[i])) {
            return false;
        }
        if (!parcel.WriteUint16(interval_[i])) {
            return false;
        }
        if (!parcel.WriteUint16(window_[i])) {
            return false;
        }
    }
    return true;
}

SeekParam *SeekParam::Unmarshalling(Parcel &parcel)
{
    SeekParam *param = new SeekParam();
    if (param != nullptr && !param->ReadFromParcel(parcel)) {
        delete param;
        param = nullptr;
    }
    return param;
}

bool SeekParam::ReadFromParcel(Parcel &parcel)
{
    if (!parcel.ReadUint8(addressType_)) {
        return false;
    }
    if (!parcel.ReadUint8(repateFilter_)) {
        return false;
    }
    if (!parcel.ReadUint8(filterPolicy_)) {
        return false;
    }
    if (!parcel.ReadUint8(phys_)) {
        return false;
    }
    for (int i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        if (!parcel.ReadUint8(type_[i])) {
            return false;
        }
        if (!parcel.ReadUint16(interval_[i])) {
            return false;
        }
        if (!parcel.ReadUint16(window_[i])) {
            return false;
        }
    }
    return true;
}

void SeekParam::ConvertToSleSeekParam(SleSeekParam &param) const
{
    param.addrType = (SleDevAddrType)addressType_;
    param.repateFilter = repateFilter_;
    param.filterPolicy = (SleSeekFilterPolicy)filterPolicy_;
    param.phys = (SleSeekPhyType)phys_;
    for(size_t i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        param.type[i] = (SleSeekType)type_[i];
        param.interval[i] = interval_[i];
        param.window[i] = window_[i];
    }
}

bool SeekParam::CheckSeekParamArray(const uint8_t type[], const uint16_t interval[], const uint16_t window[])
{
    bool allZero = true;
    for (int i = 0; i < SLE_SEEK_PHY_MAX; ++i) {
        if (type[i] != 0 || interval[i] != 0 || window[i] != 0) {
            allZero = false;
        }
    }
    if (allZero) { // 参数全0
        return false;
    }
    // 参数非全0
    bool zero[SLE_SEEK_PHY_MAX] = {false};
    for (int i = 0; i < SLE_SEEK_PHY_MAX; i++) {
        if (type[i] != SLE_PASSIVE_SEEK && type[i] != SLE_ACTIVE_SEEK) { // 扫描type
            return false;
        }
        if (interval[i] != 0 && window[i] != 0) { // 同脚标扫描间隔和窗口全不为0
            if (interval[i] < 0x0004 || interval[i] > 0xFFFF) { // 扫描间隔
                return false;
            }
            if (window[i] < 0x0004 || window[i] > 0xFFFF) { // 扫描窗口
                return false;
            }
            for (int j = 0; j < SLE_SEEK_PHY_MAX; j++) { // 扫描窗口不大于扫描间隔
                if (window[j] > interval[j]) {
                    return false;
                }
            }
            zero[i] = true;
        } else if (type[i] == 0 && interval[i] == 0 && window[i] == 0) { // 同脚标全为0
            zero[i] = true;
        }
    }
    for (int i = 0; i < SLE_SEEK_PHY_MAX; i++) { // 扫描间隔或扫描窗口为0
        if (zero[i] == false) {
            return false;
        }
    }
    return true;
}

SleErrorCode SeekParam::CheckSeekParam(const SleSeekParam &param)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(param.addrType == SLE_PUBLIC_ADDRESS_TYPE || param.addrType == SLE_RANDOM_ADDRESS_TYPE,
        SLE_ERR_INVALID_PARAM, "param seek device type error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(param.filterPolicy == SLE_SEEK_FILTER_ALL_ALLOW || param.filterPolicy == SLE_SEEK_FILTER_WLST_ALLOW,
        SLE_ERR_INVALID_PARAM, "param seek filter policy error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(param.phys == SLE_SEEK_1M_PHY || param.phys == SLE_SEEK_2M_PHY || param.phys == SLE_SEEK_4M_PHY,
        SLE_ERR_INVALID_PARAM, "param seek phys error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(CheckSeekParamArray(param.type, param.interval, param.window),
        SLE_ERR_INVALID_PARAM, "param seek phys error!");
    return SLE_NO_ERROR;
}

SeekResult::SeekResult(const SleSeekResult &result)
{
    eventType_ = result.eventType;
    SleAddress addr(result.addr);
    address_ = addr;
    SleAddress dirAddr(result.dirAddr);
    dirAddress_ = dirAddr;
    rssi_ = result.rssi;
    dataStatus_ = result.dataStatus;
    length_ = result.dataLength;
    if (result.data != nullptr && length_ != 0) {
        data_ = std::make_unique<uint8_t[]>(length_);
        (void)memcpy_s(data_.get(), length_, result.data, length_);
    } else {
        data_ = nullptr;
        length_ = 0;
    }
}

bool SeekResult::Marshalling(Parcel &parcel) const
{
    if (!parcel.WriteUint8(eventType_)) {
        return false;
    }
    if (!parcel.WriteParcelable(&address_)) {
        return false;
    }
    if (!parcel.WriteParcelable(&dirAddress_)) {
        return false;
    }
    if (!parcel.WriteUint8(rssi_)) {
        return false;
    }
    if (!parcel.WriteUint8(dataStatus_)) {
        return false;
    }
    if (!parcel.WriteUint8(length_)) {
        return false;
    }
    for (size_t i = 0; i < length_; i++) {
        if(!parcel.WriteUint8(data_[i])) {
            return false;
        }
    }
    return true;
}

SeekResult *SeekResult::Unmarshalling(Parcel &parcel)
{
    SeekResult *result = new SeekResult();
    if (result != nullptr && !result->ReadFromParcel(parcel)) {
        delete result;
        result = nullptr;
    }
    return result;
}

bool SeekResult::ReadFromParcel(Parcel &parcel)
{
    if (!parcel.ReadUint8(eventType_)) {
        return false;
    }
    std::shared_ptr<SleAddress> address(parcel.ReadParcelable<SleAddress>());
    if (!address) {
        return false;
    }
    address_ = *address;

    std::shared_ptr<SleAddress> dirAddress(parcel.ReadParcelable<SleAddress>());
    if (!dirAddress) {
        return false;
    }
    dirAddress_ = *dirAddress;

    if (!parcel.ReadUint8(rssi_)) {
        return false;
    }
    if (!parcel.ReadUint8(dataStatus_)) {
        return false;
    }
    if (!parcel.ReadUint8(length_)) {
        return false;
    }
    if (length_ != 0) {
        uint8_t data[length_];
        for (size_t i = 0; i < length_; i++) {
            if (!parcel.ReadUint8(data[i])) {
                return false;
            }
        }
        data_ = std::make_unique<uint8_t[]>(length_);
        if (memcpy_s(data_.get(), length_, data, length_) != SLE_NO_ERROR) {
            return false;
        }
    }
    return true;
}

void SeekResult::ConvertToSleSeekResult(SleSeekResult &result) const
{
    result.eventType = eventType_;
    result.addr = address_.ConvertToSleDeviceAddress();
    result.dirAddr = dirAddress_.ConvertToSleDeviceAddress();
    result.rssi = rssi_;
    result.dataStatus = dataStatus_;
    result.dataLength = length_;
    result.data = data_.get();
}
} // SleStandard
} // NearLink
} // OHOS